/*
 * Galaxium Messenger
 * Copyright (C) 2003-2007 Philippe Durand <draekz@gmail.com>
 * Copyright (C) 2007 Ben Motmans <ben.motmans@gmail.com>
 * Copyright (C) 2007 Paul Burton <paulburton89@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.IO;

using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip;

using Anculus.Core;

using Galaxium.Core;

namespace Galaxium.AdiumThemes
{
	public static class ArchiveUtility
	{
		public static bool IsFileSupported (string archive)
		{
			string lower = archive.ToLower ();
			if (lower.EndsWith (".tar.gz")
				||lower.EndsWith (".tgz")
				||lower.EndsWith (".zip"))
				return true;
			return false;
		}
		
		public static string GetArchiveNameWithoutExtension (string archive)
		{
			if (archive == null)
				throw new ArgumentNullException ("archive");
			
			archive = Path.GetFileName (archive);
			string lower = archive.ToLower ();
			int postfixLen = 0;
			
			if (lower.EndsWith (".tar.gz"))
				postfixLen = 7;
			else if (lower.EndsWith (".tgz"))
				postfixLen = 4;
			else if (lower.EndsWith (".zip"))
				postfixLen = 4;
			
			if (postfixLen > 0)
				return archive.Substring (0, archive.Length - postfixLen);
			else
				return archive;
		}
		
		public static bool Extract (string archive, string destdir)
		{
			return Extract (archive, destdir, false);
		}
		
		public static bool Extract (string archive, string destdir, bool overwrite)
		{
			FileInfo fi = new FileInfo (archive);
			if (!fi.Exists) {
				Log.Warn ("Archive '{0}' doesn't exist.");
				return false;
			}
			
			if (Directory.Exists (destdir)) {
				if (!overwrite)
					return true;
			} else {
				BaseUtility.CreateDirectoryIfNeeded (destdir);
			}
			
			string ext = fi.Extension.ToLower ();
			switch (ext) {
			case ".gz":
			case ".tgz":
				return DecompressGZip (archive, destdir);
			case ".zip":
				return DecompressZip (archive, destdir);
			default:
				Log.Warn ("Unsupported archive format '{0}'.", archive);
				return false;
			}
		}
		
		private static bool DecompressZip (string archive, string destdir)
		{
			try {
				FileStream filestream = File.OpenRead(archive);
				ZipInputStream inputstream = new ZipInputStream(filestream);
					
				ZipEntry entry;
					
				while ((entry = inputstream.GetNextEntry()) != null)
				{
					string entrydirname = Path.Combine(destdir, Path.GetDirectoryName(entry.Name));
					string entryfilename = Path.GetFileName(entry.Name);
			
					if (!string.IsNullOrEmpty(entrydirname))
						BaseUtility.CreateDirectoryIfNeeded(entrydirname);
					
					if (entryfilename != String.Empty)
					{
						try
						{
							if (entry.Size == 0)
							{
								File.Create(Path.Combine(entrydirname, entryfilename));
								continue;
							}
							
							using (FileStream streamwriter = File.Create(Path.Combine(entrydirname, entryfilename)))
							{
								int size = 2048;
								byte[] data = new byte[2048];
					
								while (true)
								{
									size = inputstream.Read(data, 0, data.Length);
								
									if (size > 0)
										streamwriter.Write(data, 0, size);
									else
										break;
								}
							}
						} catch (Exception e) {
							Log.Error (e, "Error Extracting '{0}'", entry.Name);
						}
					}
				}
					
				inputstream.Close();
				filestream.Close();
					
				return true;
			} catch (Exception e)
			{
				Log.Error(e, "Error Extracting Zip File");
				return false;
			}
		}
		
		private static bool DecompressGZip (string archive, string destdir)
		{
			try {
				FileStream filestream = File.OpenRead(archive);
				GZipInputStream inputstream = new GZipInputStream(filestream);
				ICSharpCode.SharpZipLib.Tar.TarArchive tar = TarArchive.CreateInputTarArchive(inputstream);
		
				tar.ExtractContents(destdir);
					
				tar.CloseArchive();
				inputstream.Close();
				filestream.Close();
					
				return true;
			} catch (Exception e) {
				Log.Error (e, "Error Extracting Gzipped Tar Archive");
				return false;
			}
		}
		
		public static string FindContentsDir(string dir, string pathmustcontain)
		{
			if (dir.ToLower ().Contains ("__macosx"))
				return string.Empty;
			
			if ((Path.GetFileName(dir).ToLower() == "contents") && dir.ToLower().Contains(pathmustcontain.ToLower()))
				return dir;
				
			foreach (string subdir in Directory.GetDirectories(dir))
			{
				string contentdir = FindContentsDir(subdir, pathmustcontain);
				
				if (!string.IsNullOrEmpty(contentdir))
					return contentdir;
			}
			
			return string.Empty;
		}
		
		public static string FindFile(string dir, string file, string pathmustcontain)
		{
			if (dir.ToLower().Contains(pathmustcontain.ToLower()))
			{
				foreach (string filename in Directory.GetFiles(dir))
				{
					if (Path.GetFileName(filename).ToLower() == file.ToLower())
						return filename;
						
					if (Path.GetFileNameWithoutExtension(file) == "*")
						if (Path.GetExtension(file).ToLower() == Path.GetExtension(filename).ToLower())
							return filename;
				}
			}
			
			foreach (string subdir in Directory.GetDirectories(dir))
			{
				string filename = FindFile(subdir, file, pathmustcontain);
				
				if (!string.IsNullOrEmpty(filename))
					return filename;
			}
			
			return string.Empty;
		}
		
		public static string FindDir(string dir, string pathmustcontain)
		{
			if (dir.ToLower().Contains(pathmustcontain.ToLower()))
				return dir;
			
			foreach (string subdir in Directory.GetDirectories(dir))
			{
				string directory = FindDir(subdir, pathmustcontain);
				
				if (!string.IsNullOrEmpty(directory))
					return directory;
			}
			
			return string.Empty;
		}
	}
}
